/*
  GNU  Klub RFID door, PIC program  Copyright  (C) 2008,  dule
  (http://gnu.elfak.ni.ac.yu) Dushan Vuckovic <sylphs21125@gmail.com>
  
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/



unsigned short sync_flag,     // in the sync routine if this flag is set
               one_seq,       // counts the number of 'logic one' in series
               data_in,       // gets data bit depending on data_in_1st and data_in_2nd
               cnt,           // interrupt counter
               cnt1, cnt2;    // auxiliary counters
unsigned short data_index;    // marks position in data array
char i,k;
char data[256];
char data_valid[64];
char bad_synch;               // variable for detecting bad synchronization
char buffer[7];
char rec=0;
char prijem=0;
char help[]={'K','o','m','a','n','d','e',' ','s','u',':',13,10};//'?','-','h','e','l','p',',','C','-','Z','a','t','v','a','r','a',' ','b','r','a','v','u',',','Q','-','V','r','a','c','a',' ','s','t','a','t','u','s',' ','b','r','a','v','e','.'};
char help1[]={'P','-','o','t','v','a','r','a',' ' ,'b','r','a','v','u',' ','1','0','s',13,10};
char help2[]={'P',':','X','-','d','r','z','i',' ','d','o',' ', '2','5','5','s',13,10};
char help3[]={'O','-','d','r','z','i',' ','o','t','v','o','r','e','n','u',13,10};
char help4[]={'C','-','Z','a','t','v','a','r','a',' ','b','r','a','v','u',13,10};
char help5[]={'Q','-','V','r','a','c','a',' ','s','t','a','t','u','s',13,10};
char help6[]={'?','-','h','e','l','p',13,10};
char sekunda=0;
char br_sec=0;
char mark=1;
char pov;
char pom;

void Provera(){

     if (prijem){
        prijem=0;
        if (buffer[0]=='O'){   //otvori vrata stalno
           if (buffer[1]==13){

              lata.f0=1;

              usart_write('O');
              usart_write('K');
              Usart_Write(13);                                                     // Cariage return (view ASCII chart)
              Usart_Write(10);

              }
        }else if (buffer[0]=='C'){ //Zatarabi vrata
              if (buffer[1]==13){

                 lata.f0=0;

                 usart_write('O');
                 usart_write('K');
                 Usart_Write(13);                                                     // Cariage return (view ASCII chart)
                 Usart_Write(10);

                 }
        }else if (buffer[0]=='P'){ //otvori vrata na period  od 10 sec
              if (buffer[1]==13){
                 //>>tajmer
                 lata.f0=1;
                 sekunda=0;
                 br_sec=0;
                 mark=10;
                 t2con.tmr2on=1;


                 usart_write('O');
                 usart_write('K');
                 Usart_Write(13);                                                     // Cariage return (view ASCII chart)
                 Usart_Write(10);

                
                 }else if (buffer[1]==':'){
                    k=2;
                    mark=0;
                    pov=1;
                    while (buffer[k]!=13){
                    if (buffer[k]>47) {
                       if (buffer[k]<58){
                          //izvlaci vreme u sekundama i racuna
                                    k++;
                        }
                        else{
                            k=0;
                            break;
                        }
                    }else{
                        k=0;
                        break;
                    }

                    }
                    if (k>2){
                       k--;
                       usart_write(48+k);
                       while (k>1){
                             pom=buffer[k]-48;
                             pom=pom*pov;
                             mark=mark+pom;
                             pov=pov*10;
                             k--;

                             if (pov>100) break;
                       }
                       //blok za aktiviranje tajmera
                       lata.f0=1;
                       sekunda=0;
                       br_sec=0;
                       t2con.tmr2on=1;
                       usart_write('O');
                       usart_write('K');
                       Usart_Write(13);                                                     // Cariage return (view ASCII chart)
                       Usart_Write(10);
                 }
                 else{


                        usart_write('E');
                        usart_write('R');
                        usart_write('R');
                        usart_write('O');
                        usart_write('R');
                        Usart_Write(13);                                                     // Cariage return (view ASCII chart)
                        Usart_Write(10);
                     }
                }

        }else if (buffer[0]=='Q'){   //vrati status vrata
              if (buffer[1]==13){
                 if (lata.f0)usart_write('1'); else usart_write('0');

                 Usart_Write(13);                                                     // Cariage return (view ASCII chart)
                 Usart_Write(10);
                 usart_write('O');
                 usart_write('K');
                 Usart_Write(13);                                                     // Cariage return (view ASCII chart)
                 Usart_Write(10);
               }
        }else if (buffer[0]=='?'){ //help
              if (buffer[1]==13){
                 //prikazi help poruku
                 k=0;
                 while (1){
                       usart_write(help[k]);
                       k++;
                       if (help[k]==10) break;
                       }
                k=0;
                 while (1){
                       usart_write(help1[k]);
                       k++;
                       if (help1[k]==10) break;
                       }
                k=0;
                 while (1){
                       usart_write(help2[k]);
                       k++;
                       if (help2[k]==10) break;
                       }
                      
                                              k=0;

                 while (1){
                       usart_write(help3[k]);
                       k++;
                       if (help3[k]==10) break;
                       }

                       k=0;
                 while (1){
                       usart_write(help4[k]);
                       k++;
                       if (help4[k]==10) break;
                       }
                       k=0;
                 while (1){
                       usart_write(help5[k]);
                       k++;
                       if (help5[k]==10) break;
                       }
                while (1){
                       usart_write(help6[k]);
                       k++;
                       if (help6[k]==10) break;
                       }
              }

        }else{
              usart_write('E');
              usart_write('R');
              usart_write('R');
              usart_write('O');
              usart_write('R');
              Usart_Write(13);                                                     // Cariage return (view ASCII chart)
              Usart_Write(10);

        }

        rec=0;
        }


}
void Interrupt() {
  // This is external INT1 interrupt (for sync and sample)
  //   - this interrupt is enabled once we get falling edge on RB0 (in the INT0 interrupt routine)
  //     it is enabled until we get 128 data bits

  if ((INTCON3 & 0b00001001) == 0b00001001 ) {       // if (INTCON3.INT1IF = 1) and (INTCON3.INT1IE = 1) then
      cnt++;                                         // count interrupts on INT1 pin (RB1)
      INTCON3.INT1IF = 0;
      }
  // This is external INT0 interrupt (for sync start)
  //   - once we get falling edge on RB0 we are disabling INT0 interrupt

  else if ((INTCON & 0b00010010) == 0b00010010) {    // if (INTCON.INT0IF = 1) and (INTCON.INT0IE = 1) then
     cnt = 0;
     sync_flag = 1;
     INTCON = INTCON & 0b11101101;                   // INTCON.INT0IF := 0; INTCON.INT0IE := 0;
     INTCON3.INT1IF = 0;
     INTCON3.INT1IE = 1;
   }
   //interrupt za serijsku komunikaciju
   else if (PIR1.RCIF){

        PIR1.RCIF=0;
        buffer[rec]=usart_read();
        //cisto za debagovanje
        lcd_out(1,1,buffer);
        if (buffer[rec]==13){
           prijem=1;
        }
        rec++;
        if (rec>7) rec=7;
   }
   //interrupt koji drzi otvorenu bravu
   else if (PIR1.TMR2IF) {
        PIR1.TMR2IF=0;
        sekunda++;
        if (sekunda==122) {

           br_sec++;
           sekunda=0;
           if (br_sec>=mark){
              //odbrojao je do kraja
              lata.f0=0;//zatvori vrata
              t2con.tmr2on=0; //iskljuci tajmer
              PIR1.TMR2IF=0 ;
              br_sec=0;
              sekunda=0;
              }
           }
        }
}


char CRC_Check(char *bit_array) {

char row_count, row_bit, column_count;
char row_sum, column_sum;
char row_check[5];
char column_check[11];

   // row parity check:
   row_count = 9;                     // count rows
   while (row_count < 59) {
     column_count = 0;                // count columns
     while (column_count < 5) {
       row_check[column_count] = bit_array[row_count+column_count];
       column_count++;
     }
     row_bit = 0;                     // count row bits
     row_sum = 0;
     while (row_bit < 4) {
       row_sum = row_sum + row_check[row_bit];
       row_bit++;
     }

     if (row_sum.F0 != row_check[4].F0) {
       return 0;
     }
     row_count = row_count + 5;
   }
   // end row parity check

   // column parity check
   column_count = 9;                  // count columns
   while (column_count < 13) {
     row_bit = 0;                     // count column bits
     row_count = 0;                   // count rows
     while (row_bit < 11) {
       column_check[row_bit] = bit_array[column_count+row_count];
       row_bit++;
       row_count = row_count + 5;
     }

     row_bit = 0;                     // count column bits
     column_sum = 0;
     while (row_bit < 10) {
       column_sum = column_sum + column_check[row_bit];
       row_bit++;
     }

     if (column_sum.F0 != column_check[10].F0) {
       return 0;
     }
     column_count++;
   }
   // end column parity check
   if (bit_array[63] == 1) {
     return 0;
   }
   return  1;
}

// main program
void main(){
  delay_ms(200);
  trisa=0;
  lata=0;
  ADCON1 = 0x0F;                // AD converter off
  TRISB  = 0b11110011;
  PORTB  = 0;

  USART_Init(19200);            // Initialise USART communication
  lcd_init(&portd);
  sync_flag = 0;                // sync_flag is set when falling edge on RB0 is detected
  one_seq = 0;                  // counts the number of 'logic one' in series
  data_in = 0;                  // gets data bit
  data_index = 0;               // marks position in data arrey
  cnt = 0;                      // interrupt counter
  cnt1 = 0;                     // auxiliary counter
  cnt2 = 0;
  USART_write('S');
  USART_write('Y');
  USART_write('S');
  USART_write('^');
  USART_write('S');
  USART_write('T');
  USART_write('A');
  USART_write('R');
  USART_write('T');
  Usart_Write(13);                                                     // Cariage return (view ASCII chart)
  Usart_Write(10);
  //setovanje tajmera 0
  t0con.PSA=0;
  t0con.t0PS0=1;
  t0con.t0PS1=0;
  t0con.t0PS2=1;
  //setovanje tajmera 2
  PR2=255;
  t2con=0b011111011; //prescaler*postscaler = 256
      // setup interrupts
  intcon.PEIE=1; //externi interrupt
  PIE1.tmr2ie=1;
  PIR1.RCIF=0;   //oscisti se flag
  PIE1.RCIE=1;   //aktivira se serijski interrupt
 
  //tajmer2 interrupt
  PIR1.TMR2IF=0;
  PIE2.TMR2IE=1;
 
 
  INTCON2.INTEDG0 = 0;          // Interrupt on falling edge on RB0
  INTCON2.INTEDG1 = 1;          // Interrupt on rising edge on RB1
  INTCON.INT0IF = 0;            // Clear INT0IF
  INTCON3.INT1IF = 0;           // Clear INT1IF

  INTCON.INT0IE = 0;            // turn OFF interrupt on INT0
  INTCON3.INT1IE = 0;
  INTCON.GIE = 1;               // enable GIE

  while (1) {
    bad_synch = 0;             // set bad synchronization variable to zero
    cnt = 0;                   // reseting interrupt counter
    sync_flag = 0;             // reseting sync flag
    INTCON3.INT1IF = 0;
    INTCON3.INT1IE = 0;        // disable external interrupt on RB1 (for sync and sample)
    INTCON.INT0IF = 0;
    INTCON.INT0IE = 1;         // enable external interrupt on RB0 (start sync procedure)
    while (sync_flag == 0) {   // waiting for falling edge on RB0
     provera();
    }
    while (cnt != 16) {        // waiting 16 clocks on RB1 (positioning for sampling)
     provera();
    }
    cnt = 0;
    data[0] = (PORTB.F0) & 1;
    for (data_index = 1; data_index != 0; data_index++) {   // getting 128 bits of data from RB0
      while (cnt != 32) {                                   // getting bit from RB0 every 32 clocks on RB1
        provera();
      }
      cnt = 0;                                              // reseting interrupt counter
      data[data_index] = PORTB.F0 & 1;                      // geting bit
      if(data_index & 1)
      if (!(data[data_index] ^ data[data_index-1]))
         {
            bad_synch = 1;
            break;                                          //bad synchronisation
         }
    }

    INTCON3.INT1IE = 0;                     // disable external interrupt on RB1 (for sync and sample)
    if (bad_synch)
     continue;                              // try again
    cnt1 = 0;
    one_seq = 0;
    for(cnt1 = 0; cnt1 <= 127; cnt1++) {    // we are counting 'logic one' in the data array
      if (data[cnt1 << 1] == 1) {
        one_seq++;
        }
      else {
        one_seq = 0;
        }

      if (one_seq == 9) {                  // if we get 9 'logic one' we break from the loop
          break;
    }
    }                                      //   (the position of the last  'logic one' is in the cnt1)
    if ((one_seq == 9) && (cnt1 < 73)) {   // if we got 9 'logic one' before cnt1 position 73
                                           //   we write that data into data_valid array
       data_valid[0] = 1;                  //   (it has to be before cnt1 position 73 in order
       data_valid[1] = 1;                  //    to have all 64 bits available in data array)
       data_valid[2] = 1;
       data_valid[3] = 1;
       data_valid[4] = 1;
       data_valid[5] = 1;
       data_valid[6] = 1;
       data_valid[7] = 1;
       data_valid[8] = 1;
       for(cnt2 = 9; cnt2 <= 63; cnt2++) {      // copying the rest of data from the data array into data_valid array
          cnt1++;
          data_valid[cnt2] = data[cnt1 << 1];
        }
       if (CRC_Check(data_valid) == 1) {        // if data in data_valid array pass the CRC check
            Usart_Write('C');
            Usart_Write('R');
            Usart_Write('C');
            Usart_Write(' ');
            Usart_Write('C');
            Usart_Write('H');
            Usart_Write('E');
            Usart_Write('C');
            Usart_Write('K');
            Usart_Write(' ');
            Usart_Write('O');
            Usart_Write('K');
            Usart_Write('!');
            Usart_Write(13);
            Usart_Write(10);
            for (i = 0; i <= 64; i++){                 // This part of the code
                                                       //  dislays the number of the specific RfID CARD
                if (data_valid[i] == 0) {
                  Usart_Write('0');
                  }
                else {
                  Usart_Write('1');                    // at the end of this for loop you will get a string of "0" and "1"
                  }
            }                                          //   specific to a single RfID CARD
            Usart_Write(13);                                                     // Cariage return (view ASCII chart)
            Usart_Write(10);                           // Line Feed (view ASCII chart)
            //delay od 500ms
            TMR0H=0;
            TMR0L=0;
            T0CON.TMR0ON=1;
            while (!(INTCON.TMR0IF)){
                  provera();
                  }
            T0CON.TMR0ON=0;
            INTCON.TMR0IF=0;
        }
     }

   }


}
